/*
 * Filename : Attack.java
 * Description : **COMPLETE**
 * History  : Date               Engineer       Description
 *            ---------          ------------   --------------------------------
 *            August 21, 2007      waitek         Initial creation.
 */

package com.lct.eq2.data;

import java.io.Serializable;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;

public class Attack extends LogEvent implements Serializable, Comparator {
    static final long serialVersionUID = 754194893450739751L;
    
    public static final int AVOID_TYPE_NO_DAMAGE = -15;
    public static final int AVOID_TYPE_COUNTER = -14;
    public static final int AVOID_TYPE_MELEE_REFLECT = -13;
    public static final int AVOID_TYPE_MELEE_DEFLECT = -12;
    public static final int AVOID_TYPE_MELEE_RIPOSTE = -11;
    public static final int AVOID_TYPE_MELEE_DODGE = -10;
    public static final int AVOID_TYPE_MELEE_PARRY = -9;
    public static final int AVOID_TYPE_BLOCK = -8;
    public static final int AVOID_TYPE_MELEE_MISS = -7;
    public static final int AVOID_TYPE_NON_MELEE_RESIST = -6;
    public static final int AVOID_TYPE_NON_MELEE_REFLECT = -5;
    public static final int ATTACK_TYPE_NON_MELEE = -4;
    public static final int ATTACK_TYPE_MELEE = -3;
    public static final int ATTACK_TYPE_OUTGOING = -2;
    public static final int ATTACK_TYPE_SELF = -16;
    public static final int ATTACK_TYPE_INCOMING = -1;
    public static final int ATTACK_TYPE_MELEE_PIERCE = 0;
    public static final int ATTACK_TYPE_MELEE_CRUSH = 1;
    public static final int ATTACK_TYPE_MELEE_SLASH = 2;
    public static final int ATTACK_TYPE_MELEE_MELEE = 3;
    public static final int ATTACK_TYPE_NON_MELEE_HEAT = 4;
    public static final int ATTACK_TYPE_NON_MELEE_COLD = 5;
    public static final int ATTACK_TYPE_NON_MELEE_POISON = 6;
    public static final int ATTACK_TYPE_NON_MELEE_DISEASE = 7;
    public static final int ATTACK_TYPE_NON_MELEE_MAGIC = 8;
    public static final int ATTACK_TYPE_NON_MELEE_MENTAL = 9;
    public static final int ATTACK_TYPE_UNKNOWN = 10;
    public static final int ATTACK_TYPE_NON_MELEE_HEALING = 11;
    public static final int ATTACK_TYPE_NON_MELEE_DIVINE = 12;
    public static final int ATTACK_TYPE_NON_MELEE_FOCUS = 13;
//    public static final int ATTACK_TYPE_NON_MELEE_SPIRIT = 13;
//    public static final int ATTACK_TYPE_NON_MELEE_CORPOREAL = 14;
//    public static final int ATTACK_TYPE_NON_MELEE_ELEMENTAL = 15;
    
    private static HashMap attackTypes;
    private static HashMap<Integer, String> attackTypeNames;
    
    private boolean isCrit;
    private boolean isDoubleAttack;
    private boolean isAOEAttack;
    private boolean avoided;
    private boolean isHeal;
    private int reasonAvoided;
    private int direction;
    private int attackTypeInt = 99;
    private String attacker;
    private String target;
    private String attackType;
    private String attackName;
    private String originatingServer;
    private int amountOfAttackDamage;
    private long timeStamp;
    private long localTime;
    private double remoteDelta;
    private double localDelta;
    private long serverTime;
    private int amountMitigated;
    private boolean trueSelf = false;
    private String mainCharacter;
    private String line;
    
    /** Creates a new instance of Attack */
    public Attack() {
        this.isCrit = false;
        this.isHeal = false;
        this.attackType = "Unknown";
        this.initAttackTypes();
    }

    public void initAttackTypes() {
        attackTypes = new HashMap();
        attackTypeNames = new HashMap();
        
        attackTypes.put("Unknown", Attack.ATTACK_TYPE_UNKNOWN);
        attackTypes.put("crush", Attack.ATTACK_TYPE_MELEE_CRUSH);
        attackTypes.put("crushing", Attack.ATTACK_TYPE_MELEE_CRUSH);
        attackTypes.put("pierce", Attack.ATTACK_TYPE_MELEE_PIERCE);
        attackTypes.put("piercing", Attack.ATTACK_TYPE_MELEE_PIERCE);
        attackTypes.put("slash", Attack.ATTACK_TYPE_MELEE_SLASH);
        attackTypes.put("slashing", Attack.ATTACK_TYPE_MELEE_SLASH);
        attackTypes.put("heat", Attack.ATTACK_TYPE_NON_MELEE_HEAT);
        attackTypes.put("burn", Attack.ATTACK_TYPE_NON_MELEE_HEAT);
        attackTypes.put("burns", Attack.ATTACK_TYPE_NON_MELEE_HEAT);
        attackTypes.put("cold", Attack.ATTACK_TYPE_NON_MELEE_COLD);
        attackTypes.put("freeze", Attack.ATTACK_TYPE_NON_MELEE_COLD);
        attackTypes.put("freezes", Attack.ATTACK_TYPE_NON_MELEE_COLD);
        attackTypes.put("poison", Attack.ATTACK_TYPE_NON_MELEE_POISON);
        attackTypes.put("poisons", Attack.ATTACK_TYPE_NON_MELEE_POISON);
        attackTypes.put("disease", Attack.ATTACK_TYPE_NON_MELEE_DISEASE);
        attackTypes.put("diseases", Attack.ATTACK_TYPE_NON_MELEE_DISEASE);
        attackTypes.put("magic", Attack.ATTACK_TYPE_NON_MELEE_MAGIC);
        attackTypes.put("zap", Attack.ATTACK_TYPE_NON_MELEE_MAGIC);
        attackTypes.put("zaps", Attack.ATTACK_TYPE_NON_MELEE_MAGIC);
        attackTypes.put("mental", Attack.ATTACK_TYPE_NON_MELEE_MENTAL);
        attackTypes.put("confound", Attack.ATTACK_TYPE_NON_MELEE_MENTAL);
        attackTypes.put("confounds", Attack.ATTACK_TYPE_NON_MELEE_MENTAL);
        attackTypes.put("divine", Attack.ATTACK_TYPE_NON_MELEE_DIVINE);
        attackTypes.put("smite", Attack.ATTACK_TYPE_NON_MELEE_DIVINE);
        attackTypes.put("smites", Attack.ATTACK_TYPE_NON_MELEE_DIVINE);
        attackTypes.put("focus", Attack.ATTACK_TYPE_NON_MELEE_FOCUS);
        attackTypes.put("unknown", Attack.ATTACK_TYPE_UNKNOWN);
        attackTypes.put("miss", Attack.AVOID_TYPE_MELEE_MISS);
        attackTypes.put("misses", Attack.AVOID_TYPE_MELEE_MISS);
        attackTypes.put("resist", Attack.AVOID_TYPE_NON_MELEE_RESIST);
        attackTypes.put("resists", Attack.AVOID_TYPE_NON_MELEE_RESIST);
        attackTypes.put("block", Attack.AVOID_TYPE_BLOCK);
        attackTypes.put("blocks", Attack.AVOID_TYPE_BLOCK);
        attackTypes.put("blocked", Attack.AVOID_TYPE_BLOCK);
        attackTypes.put("dodge", Attack.AVOID_TYPE_MELEE_DODGE);
        attackTypes.put("dodges", Attack.AVOID_TYPE_MELEE_DODGE);
        attackTypes.put("dodged", Attack.AVOID_TYPE_MELEE_DODGE);
        attackTypes.put("disrupts", Attack.AVOID_TYPE_NON_MELEE_RESIST);
        attackTypes.put("disrupted", Attack.AVOID_TYPE_NON_MELEE_RESIST);
        attackTypes.put("parry", Attack.AVOID_TYPE_MELEE_PARRY);
        attackTypes.put("parries", Attack.AVOID_TYPE_MELEE_PARRY);
        attackTypes.put("parried", Attack.AVOID_TYPE_MELEE_PARRY);
        attackTypes.put("riposte", Attack.AVOID_TYPE_MELEE_RIPOSTE);
        attackTypes.put("ripostes", Attack.AVOID_TYPE_MELEE_RIPOSTE);
        attackTypes.put("deflect", Attack.AVOID_TYPE_MELEE_DEFLECT);
        attackTypes.put("bashes", Attack.AVOID_TYPE_MELEE_DEFLECT);
        attackTypes.put("bash", Attack.AVOID_TYPE_MELEE_DEFLECT);
        attackTypes.put("deflects", Attack.AVOID_TYPE_MELEE_DEFLECT);
        attackTypes.put("healing", Attack.ATTACK_TYPE_NON_MELEE_HEALING);
        attackTypes.put("reflect", Attack.AVOID_TYPE_NON_MELEE_REFLECT);
        attackTypes.put("reflects", Attack.AVOID_TYPE_NON_MELEE_REFLECT);
        attackTypes.put("counters", Attack.AVOID_TYPE_COUNTER);
        
        attackTypeNames.put(Attack.AVOID_TYPE_COUNTER, "counter");
        attackTypeNames.put(Attack.AVOID_TYPE_MELEE_RIPOSTE, "riposte");
        attackTypeNames.put(Attack.AVOID_TYPE_NON_MELEE_REFLECT, "reflect");
        attackTypeNames.put(Attack.AVOID_TYPE_MELEE_DEFLECT, "deflect");
        attackTypeNames.put(Attack.AVOID_TYPE_MELEE_PARRY, "parry");
        attackTypeNames.put(Attack.AVOID_TYPE_MELEE_DODGE, "dodge");
        attackTypeNames.put(Attack.AVOID_TYPE_BLOCK, "block");
        attackTypeNames.put(Attack.AVOID_TYPE_MELEE_MISS, "miss");
        attackTypeNames.put(Attack.ATTACK_TYPE_NON_MELEE_FOCUS, "focus");
        attackTypeNames.put(Attack.ATTACK_TYPE_NON_MELEE_DIVINE, "divine");
        attackTypeNames.put(Attack.ATTACK_TYPE_NON_MELEE_MENTAL, "mental");
        attackTypeNames.put(Attack.ATTACK_TYPE_NON_MELEE_MAGIC, "magic");
        attackTypeNames.put(Attack.ATTACK_TYPE_NON_MELEE_DISEASE, "disease");
        attackTypeNames.put(Attack.ATTACK_TYPE_NON_MELEE_POISON, "poison");
        attackTypeNames.put(Attack.ATTACK_TYPE_NON_MELEE_COLD, "cold");
        attackTypeNames.put(Attack.ATTACK_TYPE_NON_MELEE_HEAT, "heat");
        attackTypeNames.put(Attack.ATTACK_TYPE_MELEE_SLASH, "slashing");
        attackTypeNames.put(Attack.ATTACK_TYPE_MELEE_PIERCE, "piercing");
        attackTypeNames.put(Attack.ATTACK_TYPE_MELEE_CRUSH, "crushing");
        attackTypeNames.put(Attack.ATTACK_TYPE_NON_MELEE_HEALING, "healing");
    }

    public long getServerTime() {
        return serverTime;
    }

    public void setServerTime(long serverTime) {
        this.serverTime = serverTime;
    }

    public long getLocalTime() {
        return localTime;
    }

    public void setLocalTime(long localTime) {
        this.localTime = localTime;
    }

    public double getLocalDelta() {
        return localDelta;
    }

    public void setLocalDelta(double localDelta) {
        this.localDelta = localDelta;
    }

    public double getRemoteDelta() {
        return remoteDelta;
    }

    public void setRemoteDelta(double remoteDelta) {
        this.remoteDelta = remoteDelta;
    }

    public String getLine() {
        return line;
    }

    public void setLine(String line) {
        this.line = line;
    }

    public String getMainCharacter() {
        String rval = null;
        if (this.originatingServer.equalsIgnoreCase("Battlegrounds")) {
            rval = this.originatingServer + "." + this.mainCharacter;
        } else {
            rval = mainCharacter;
        }
        return rval;
    }

    public void setMainCharacter(String mainCharacter) {
        this.mainCharacter = mainCharacter;
    }

    public boolean isTrueSelf() {
        return this.trueSelf;
    }

    public void setTrueSelf(boolean trueSelf) {
        this.trueSelf = trueSelf;
    }

    public boolean isAOEAttack() {
        return isAOEAttack;
    }

    public void setIsAOEAttack(boolean isAOEAttack) {
        this.isAOEAttack = isAOEAttack;
    }

    public boolean isDoubleAttack() {
        return isDoubleAttack;
    }

    public void setIsDoubleAttack(boolean isDoubleAttack) {
        this.isDoubleAttack = isDoubleAttack;
    }

    public int getAmountMitigated() {
        return amountMitigated;
    }

    public void setAmountMitigated(int amountMitigated) {
        this.amountMitigated = amountMitigated;
    }
    
    public void setOriginatingServer(String server) {
        this.originatingServer = server;
    }
    
    public String getOriginatingServer() {
        return this.originatingServer;
    }
    
    public void setAvoided(boolean avoided, String reason) {
        this.reasonAvoided = ((Integer)attackTypes.get(reason));
        this.avoided = avoided;
    }
    
    public void setAvoided(boolean avoided, int reason) {
        this.reasonAvoided = reason;
        this.avoided = avoided;
    }
    
    public int getReasonAvoided() {
        return this.reasonAvoided;
    }

    public String getReasonAvoidedString() {
        String rval = null;
        switch (this.reasonAvoided) {
            case Attack.AVOID_TYPE_COUNTER:
                rval = "counter";
                break;
            case Attack.AVOID_TYPE_BLOCK:
                rval = "block";
                break;
            case Attack.AVOID_TYPE_MELEE_DEFLECT:
                rval = "deflect";
                break;
            case Attack.AVOID_TYPE_MELEE_DODGE:
                rval = "dodge";
                break;
            case Attack.AVOID_TYPE_MELEE_MISS:
                rval = "miss";
                break;
            case Attack.AVOID_TYPE_MELEE_PARRY:
                rval = "parry";
                break;
            case Attack.AVOID_TYPE_MELEE_REFLECT:
                rval = "melee reflect";
                break;
            case Attack.AVOID_TYPE_MELEE_RIPOSTE:
                rval = "riposte";
                break;
            case Attack.AVOID_TYPE_NON_MELEE_REFLECT:
                rval = "spell reflect";
                break;
            case Attack.AVOID_TYPE_NON_MELEE_RESIST:
                rval = "spell resist";
                break;
            case Attack.AVOID_TYPE_NO_DAMAGE:
                rval = "stoneskin";
                break;
            default:
                break;
        }
        return rval;
    }

    public boolean isAvoided() {
        return this.avoided;
    }
    
    public boolean isCrit() {
        return this.isCrit;
    }
    
    public boolean isHeal() {
        boolean rval = false;
        try {
            int tmp = this.getAttackTypeInt();
            if (tmp == Attack.ATTACK_TYPE_NON_MELEE_HEALING) {
                rval = true;
            }
        } catch (NullPointerException e) {
            // nothing
        }
        return rval;
    }
    
    public boolean isMelee() {
        boolean rval = false;
        if (this.getAttackClassInt() == Attack.ATTACK_TYPE_MELEE) {
            rval = true;
        }
        return rval;
    }
    
    public boolean isNonMelee() {
        boolean rval = false;
        if (this.getAttackClassInt() == Attack.ATTACK_TYPE_NON_MELEE) {
            rval = true;
        }
        return rval;
    }
    
    public boolean isUnknown() {
        boolean rval = false;
        if (this.getAttackClassInt() == Attack.ATTACK_TYPE_UNKNOWN) {
            rval = true;
        }
        return rval;
    }
    
    public void setIsCrit(boolean crit) {
        this.isCrit = crit;
    }
    
    public void setAmountOfAttackDamage(int amount) {
        this.amountOfAttackDamage = amount;
    }
    
    public void setTarget(String target) {
        this.target = target;
    }
    
    public void setAttacker(String attacker) {
        this.attacker = attacker;
    }
    
    public void setAttackName(String attackName) {
        this.attackName = attackName;
    }

    public String getAttackTypeFromHashMap(int attackType) {
        String rval = "Unknown";
        if (this.attackTypeNames.containsKey(attackType)) {
            rval = this.attackTypeNames.get(attackType);
        }
        return rval;
    }

    public void setAttackType(String attackType) {
        this.attackType = attackType;
    }
    
    public void setAttackTypeInt(int attackType) {
        this.attackTypeInt = attackType;
    }
    
    public void setAttackDirection(int direction) {
        this.direction = direction;
    }
    
    @Override
    public String toString() {
        String rval = null;
        if (this.attackName == null) {
            rval = this.getAttackType();
        } else {
            rval = this.getAttackName();
        }
        return rval;
    }
    
    public String getAttackDirectionRelativeToCombatant(Combatant combatant) {
        String rval = null;
        if (!this.attacker.equalsIgnoreCase(this.target)) {
            if (combatant.getName().equalsIgnoreCase(this.target)) {
                rval = "Incoming";
            } else {
                rval = "Outgoing";
            }
        } else {
            rval = "Self";
        }
        return rval;
    }
    
    public String getAttackDirectionRelativeToCombatant(String combatant) {
        String rval = null;
        if (!this.attacker.equalsIgnoreCase(this.target)) {
            if (combatant.equalsIgnoreCase(this.target)) {
                rval = "Incoming";
            } else {
                rval = "Outgoing";
            }
        } else {
            rval = "Self";
        }
        return rval;
    }
    
    public int getAttackDirectionIntRelativeToCombatant(String name) {
        int rval;

        if (name.equalsIgnoreCase(attacker) && name.equalsIgnoreCase(target)) {
            rval = Attack.ATTACK_TYPE_SELF;
        } else if (name.equalsIgnoreCase(this.target)) {
            rval = Attack.ATTACK_TYPE_INCOMING;
        } else {
            rval = Attack.ATTACK_TYPE_OUTGOING;
        }
        return rval;
    }
    
    public int getAttackDirectionIntRelativeToCombatant(Combatant combatant) {
        int rval;
        String name = combatant.getName();

        if (name.equalsIgnoreCase(attacker) && name.equalsIgnoreCase(target)) {
            rval = Attack.ATTACK_TYPE_SELF;
        } else if (name.equalsIgnoreCase(this.target)) {
            rval = Attack.ATTACK_TYPE_INCOMING;
        } else {
            rval = Attack.ATTACK_TYPE_OUTGOING;
        }
        return rval;
    }
    
    @Override
    public void setTimeStamp(long timeStamp) {
        this.timeStamp = timeStamp;
    }
    
    @Override
    public long getTimeStamp() {
        return this.timeStamp;
    }
    
    public String getAttackDirection() {
        String rval = null;
        switch (this.direction) {
            case Attack.ATTACK_TYPE_INCOMING:
                rval = "Incoming";
                break;
            case Attack.ATTACK_TYPE_OUTGOING:
                rval = "Outgoing";
                break;
            case Attack.ATTACK_TYPE_SELF:
                rval = "Self";
            default:
                break;
        }
        return rval;
    }
    
    public int getAttackDirectionInt() {
        return this.direction;
    }
    
    public String getAttackClass() {
        String rval = null;
        try {
            if (((Integer)attackTypes.get(this.attackType)).intValue() > 3) {
                rval = "Non-Melee";
            } else {
                rval = "Melee";
            }
        } catch (NullPointerException ex) {
            System.out.println(this.attackType);
            ex.printStackTrace();
        }
        return rval;
    }

    public int getAttackClassInt() {
        int rval = -99;
        if (this.attackTypeInt != 99) {
            rval = this.attackTypeInt;
        } else {
            try {
                int type = ((Integer)attackTypes.get(this.attackType)).intValue();
                if ((type >= 4 && type <= 9) || (type >= 12 && type <= 15)) {
                    rval = Attack.ATTACK_TYPE_NON_MELEE;
                    this.attackTypeInt = Attack.ATTACK_TYPE_NON_MELEE;
                } else if (type <= 3 && type >= 0) {
                    rval = Attack.ATTACK_TYPE_MELEE;
                    this.attackTypeInt = Attack.ATTACK_TYPE_MELEE;
                } else if (type == 11) {
                    rval = Attack.ATTACK_TYPE_NON_MELEE_HEALING;
                    this.attackTypeInt = Attack.ATTACK_TYPE_NON_MELEE_HEALING;
                }
            } catch (NullPointerException e) {
                System.out.println("Attacker = " + this.getAttacker() + ", Target = " + this.getTarget() + " attack name = " + 
                        this.getAttackName() + ", attack type = " + this.getAttackType());
                //e.printStackTrace();
            }
        }
        return rval;
    }
    
    public String getAttackName() {
        String rval = null;
        if (this.attackTypes.containsKey(this.attackName)) {
            rval = this.getAttackTypeFromHashMap(this.getAttackTypeInt());
        } else {
            rval = this.attackName;
        }
        return rval;
    }
    
    public String getAttackType() {
        return this.getAttackTypeFromHashMap(this.getAttackTypeInt());
    }
    
    public int getAttackTypeInt() {
        int rval = 10;
        try {
            if (this.attackTypes == null) {
                this.initAttackTypes();
            }
            rval = (Integer)this.attackTypes.get(this.attackType);
        } catch (NullPointerException e) {
            System.out.println("ERROR, line: " + this.getLine());
//            System.out.println("ERROR with attack: timestamp = " + new Date(this.getTimeStamp()) + ", attacker = " + this.getAttacker() + ", target = " +
//                    this.getTarget() + ", attack name = " + this.getAttackName() + ", attack type = " + this.attackType);
            e.printStackTrace();
        }
        return rval;
    }
    
    public String getTarget() {
        return this.target;
    }
    
    public int getAmountOfAttackDamage() {
        return this.amountOfAttackDamage;
    }
    
    public String getAttacker() {
        return this.attacker;
    }

    public int compare(Object o1, Object o2) {
        int rval = 0;
        Attack a1 = (Attack)o1;
        Attack a2 = (Attack)o2;
        if (a1.getTimeStamp() > a2.getTimeStamp()) {
            rval = 1;
        } else if (a1.getTimeStamp() < a2.getTimeStamp()) {
            rval = -1;
        } else {
            rval = 0;
        }
        return rval;
    }

    @Override
    public boolean equals(Object o) {
        boolean rval = false;
        Attack a = null;
        if (o instanceof Attack) {
            a = (Attack)o;
            if (a.hashCode() == this.hashCode()) {
                long _timeStamp = a.getTimeStamp();
                double _delta = this.getTimeStamp() - _timeStamp;
                if ((_delta >= 0) && (_delta <= 500)) {
                    rval = true;
                }
            }
        }

        return rval;
    }

    @Override
    public int hashCode() {
        int hash = 3;
        hash = 43 * hash + (this.isCrit ? 1 : 0);
        hash = 43 * hash + (this.isDoubleAttack ? 1 : 0);
        hash = 43 * hash + (this.isAOEAttack ? 1 : 0);
        hash = 43 * hash + (this.avoided ? 1 : 0);
        hash = 43 * hash + (this.isHeal ? 1 : 0);
        hash = 43 * hash + this.reasonAvoided;
        hash = 43 * hash + this.direction;
        hash = 43 * hash + this.attackTypeInt;
        hash = 43 * hash + (this.attacker != null ? this.attacker.hashCode() : 0);
        hash = 43 * hash + (this.target != null ? this.target.hashCode() : 0);
        hash = 43 * hash + (this.attackType != null ? this.attackType.hashCode() : 0);
        hash = 43 * hash + (this.attackName != null ? this.attackName.hashCode() : 0);
        hash = 43 * hash + (this.originatingServer != null ? this.originatingServer.hashCode() : 0);
        hash = 43 * hash + this.amountOfAttackDamage;
        return hash;
    }
}